import { toPercent } from './utils';

interface FileSplitReaderOptions {
  chunkSize: number;
  file: File;
}

interface FilePieceOptions {
  start: number;
  end: number;
  index: number;
}

class FilePiece {
  private readonly size: number;
  private readonly start: number;
  private readonly end: number;
  private readonly index = 0;

  constructor(options: FilePieceOptions) {
    Object.assign(this, options);
    this.size = this.end - this.start;
  }

  getStart() {
    return this.start;
  }

  getEnd() {
    return this.end;
  }

  getSize() {
    return this.size;
  }
}
export interface ReadResult<T = ArrayBuffer> {
  piece: FilePiece;
  data: T;
}

export default class FileSplitReader {
  static read(file: File, start: number, end: number) {
    return new Promise<Uint8Array>((resolve) => {
      const fileReader = new FileReader();
      fileReader.onerror = () => {
        // console.log('onerror');
      };
      fileReader.onabort = () => {
        // console.log('onerror');
      };
      fileReader.onprogress = (e) => {
        const progress = e.loaded / e.total;
        const percent = toPercent(progress);
        // console.log('progress:', percent);
      };
      fileReader.onload = (e) => {
        if (fileReader.readyState === FileReader.DONE) {
          const results = fileReader.result as ArrayBuffer;
          const arr = new Uint8Array(results);
          resolve(arr);
        }
      };
      // 读取指定字节区间文件
      const blob = file.slice(start, end);
      fileReader.readAsArrayBuffer(blob);
    });
  }

  static readAsBase64String(file: File, start: number, end: number) {
    return FileSplitReader.read(file, start, end).then((res) =>
      btoa(res.reduce((acc, c) => `${acc}${String.fromCharCode(c)}`, ''))
    );
  }

  private chunkSize: number;
  private file: File;
  private readonly filesize: number = 0;
  private range: FilePiece[] = [];

  constructor(options: FileSplitReaderOptions) {
    Object.assign(this, options);
    this.filesize = this.file.size;
    this.init();
  }

  getChunkCount() {
    return this.range.length;
  }

  init() {
    const restSize = this.filesize % this.chunkSize;
    let index = 0;
    for (let i = 0; i < this.filesize - restSize; i += this.chunkSize) {
      this.range.push(
        new FilePiece({
          start: i,
          index: index++,
          end: i + this.chunkSize,
        })
      );
    }
    if (restSize > 0) {
      this.range.push(
        new FilePiece({
          start: this.filesize - restSize,
          end: this.filesize,
          index: index + 1,
        })
      );
    }
  }

  read(index: number): Promise<ReadResult<Uint8Array>>;
  read(index: number[]): Promise<ReadResult<Uint8Array>[]>;
  read(index: number | number[]) {
    if (Array.isArray(index)) {
      return Promise.all<ReadResult<Uint8Array>>(index.map((i) => this.read(i)));
    }

    const piece = this.range[index];
    return FileSplitReader.read(this.file, piece.getStart(), piece.getEnd()).then((res) => {
      return {
        piece,
        data: res,
      };
    });
  }

  readAsBase64String(index: number): Promise<ReadResult<string>>;
  readAsBase64String(index: number[]): Promise<ReadResult<string>[]>;
  readAsBase64String(index: number | number[]) {
    if (Array.isArray(index)) {
      return Promise.all(index.map((i) => this.readAsBase64String(i)));
    }
    const piece = this.range[index];
    return FileSplitReader.readAsBase64String(this.file, piece.getStart(), piece.getEnd()).then((res) => {
      return {
        piece,
        data: res,
      };
    });
  }
}
